前端:Vue3 对比 Vue2 有哪些变化? 您所在的位置:网站首页 vue 移动dom位置 前端:Vue3 对比 Vue2 有哪些变化?

前端:Vue3 对比 Vue2 有哪些变化?

2023-03-16 06:38| 来源: 网络整理| 查看: 265

前言

希望本篇文章能帮你加深对 Vue 的理解,能信誓旦旦地说自己熟练Vue2/3。

内容混杂用法 + 原理 + 使用小心得,建议收藏,慢慢看。

区别生命周期的变化

整体来看,变化不大,只是名字大部分需要 + on,功能上类似。使用上 Vue3 组合式 API 需要先引入;Vue2 选项 API 则可直接调用,如下所示。

//vue3

import{onMounted}from'vue'

onMounted(()=>{

...

})

//可将不同的逻辑拆开成多个onMounted,依然按顺序执行,不被覆盖

onMounted(()=>{

...

})

//vue2

exportdefault{

mounted(){

...

},

}

常用生命周期表格如下所示。

Tips: setup是围绕beforeCreate和created生命周期钩子运行的,所以不需要显式地去定义。

多根节点

Vue3 支持了多根节点组件,也就是fragment。

Vue2中,编写页面的时候,我们需要去将组件包裹在中,否则报错警告。

...

...

...

Vue3,我们可以组件包含多个根节点,可以少写一层,niceeee !

...

...

...

异步组件

Vue3 提供 Suspense组件,允许程序在等待异步组件时渲染兜底的内容,如 loading ,使用户体验更平滑。 使用它,需在模板中声明,并包括两个命名插槽:default和fallback。Suspense确保加载完异步内容时显示默认插槽,并将fallback插槽用作加载状态。

Loading...

真实的项目中踩过坑,若想在 setup 中调用异步请求,需在 setup 前加async关键字。这时,会受到警告async setup() is used without a suspense boundary。

解决方案:在父页面调用当前组件外包裹一层Suspense组件。

Teleport

Vue3 提供Teleport组件可将部分DOM移动到 Vue app之外的位置。比如项目中常见的Dialog组件。

点击

组合式API

Vue2 是 选项式API(Option API),一个逻辑会散乱在文件不同位置(data、props、computed、watch、生命周期函数等),导致代码的可读性变差,需要上下来回跳转文件位置。Vue3 组合式API(Composition API)则很好地解决了这个问题,可将同一逻辑的内容写到一起。

除了增强了代码的可读性、内聚性,组合式API 还提供了较为完美的逻辑复用性方案,举个 ,如下所示公用鼠标坐标案例。

//main.vue

mouseposition{{x}}{{y}}

import{ref}from'vue'

importuseMousePositionfrom'./useMousePosition'

const{x,y}=useMousePosition()

}

//useMousePosition.js

import{ref,onMounted,onUnmounted}from'vue'

functionuseMousePosition(){

letx=ref(0)

lety=ref(0)

functionupdate(e){

x.value=e.pageX

y.value=e.pageY

}

onMounted(()=>{

window.addEventListener('mousemove',update)

})

onUnmounted(()=>{

window.removeEventListener('mousemove',update)

})

return{

x,

y

}

}

解决了 Vue2 Mixin的存在的命名冲突隐患,依赖关系不明确,不同组件间配置化使用不够灵活。

响应式原理

Vue2 响应式原理基础是Object.defineProperty;Vue3 响应式原理基础是 Proxy。

Object.defineProperty

基本用法:直接在一个对象上定义新的属性或修改现有的属性,并返回对象。

Tips: writable 和 value 与 getter 和 setter 不共存。

letobj={}

letname='瑾行'

Object.defineProperty(obj,'name',{

enumerable:true,//可枚举(是否可通过for...in或Object.keys()进行访问)

configurable:true,//可配置(是否可使用delete删除,是否可再次设置属性)

//value:'',//任意类型的值,默认undefined

//writable:true,//可重写

get:function(){

returnname

},

set:function(value){

name=value

}

})

搬运 Vue2 核心源码,略删减。

functiondefineReactive(obj,key,val){

//一key一个dep

constdep=newDep()

//获取key的属性描述符,发现它是不可配置对象的话直接return

constproperty=Object.getOwnPropertyDescriptor(obj,key)

if(property&&property.configurable===false){return}

//获取getter和setter,并获取val值

constgetter=property&&property.get

constsetter=property&&property.set

if((!getter||setter)&&arguments.length===2){val=obj[key]}

//递归处理,保证对象中所有key被观察

letchildOb=observe(val)

Object.defineProperty(obj,key,{

enumerable:true,

configurable:true,

//get劫持obj[key]的进行依赖收集

get:functionreactiveGetter(){

constvalue=getter?getter.call(obj):val

if(Dep.target){

//依赖收集

dep.depend()

if(childOb){

//针对嵌套对象,依赖收集

childOb.dep.depend()

//触发数组响应式

if(Array.isArray(value)){

dependArray(value)

}

}

}

}

returnvalue

})

//set派发更新obj[key]

set:functionreactiveSetter(newVal){

...

if(setter){

setter.call(obj,newVal)

}else{

val=newVal

}

//新值设置响应式

childOb=observe(val)

//依赖通知更新

dep.notify()

}

}

那 Vue3 为何会抛弃它呢?那肯定是有一些缺陷的。

主要原因:无法监听对象或数组新增、删除的元素。Vue2 方案:针对常用数组原型方法push、pop、shift、unshift、splice、sort、reverse进行了hack处理;提供Vue.set监听对象/数组新增属性。对象的新增/删除响应,还可以new个新对象,新增则合并新属性和旧对象;删除则将删除属性后的对象深拷贝给新对象。

Tips: Object.defineOProperty是可以监听数组已有元素,但 Vue2 没有提供的原因是性能问题,具体可看见参考第二篇 ~。

Proxy

Proxy是ES6新特性,通过第2个参数handler拦截目标对象的行为。相较于Object.defineProperty提供语言全范围的响应能力,消除了局限性。但在兼容性上放弃了(IE11以下)

局限性

对象/数组的新增、删除。监测.length修改。Map、Set、WeakMap、WeakSet的支持。

基本用法:创建对象的代理,从而实现基本操作的拦截和自定义操作。

consthandler={

get:function(obj,prop){

returnpropinobj?obj[prop]:''

},

set:function(){},

...

}

搬运 Vue3 的源码 reactive.ts 文件

functioncreateReactiveObject(target,isReadOnly,baseHandlers,collectionHandlers,proxyMap){

...

//collectionHandlers:处理Map、Set、WeakMap、WeakSet

//baseHandlers:处理数组、对象

constproxy=newProxy(

target,

targetType===TargetType.COLLECTION?collectionHandlers:baseHandlers

)

proxyMap.set(target,proxy)

returnproxy

}

以 baseHandlers.ts 为例,使用Reflect.get而不是target[key]的原因是receiver参数可以把this指向getter调用时,而非Proxy构造时的对象。

//依赖收集

functioncreateGetter(isReadonly=false,shallow=false){

returnfunctionget(target:Target,key:string|symbol,receiver:object){

...

//数组类型

consttargetIsArray=isArray(target)

if(!isReadonly&&targetIsArray&&hasOwn(arrayInstrumentations,key)){

returnReflect.get(arrayInstrumentations,key,receiver)

}

//非数组类型

constres=Reflect.get(target,key,receiver);

//对象递归调用

if(isObject(res)){

returnisReadonly?readonly(res):reactive(res)

}

returnres

}

}

//派发更新

functioncreateSetter(){

returnfunctionset(target:Target,key:string|symbol,value:unknown,receiver:Object){

value=toRaw(value)

oldValue=target[key]

//因ref数据在setvalue时就已trigger依赖了,所以直接赋值return即可

if(!isArray(target)&&isRef(oldValue)&&!isRef(value)){

oldValue.value=value

returntrue

}

//对象是否有key有keyset,无keyadd

consthadKey=hasOwn(target,key)

constresult=Reflect.set(target,key,value,receiver)

if(target===toRaw(receiver)){

if(!hadKey){

trigger(target,TriggerOpTypes.ADD,key,value)

}elseif(hasChanged(value,oldValue)){

trigger(target,TriggerOpTypes.SET,key,value,oldValue)

}

}

returnresult

}

}

虚拟DOM

Vue3 相比于 Vue2 虚拟DOM 上增加patchFlag字段。我们借助Vue3 Template Explorer来看。

技术摸鱼

今天天气真不错

{{name}}

渲染函数如下。

import{createElementVNodeas_createElementVNode,toDisplayStringas_toDisplayString,openBlockas_openBlock,createElementBlockas_createElementBlock,pushScopeIdas_pushScopeId,popScopeIdas_popScopeId}from"vue"

const_withScopeId=n=>(_pushScopeId("scope-id"),n=n(),_popScopeId(),n)

const_hoisted_1={id:"app"}

const_hoisted_2=/*#__PURE__*/_withScopeId(()=>/*#__PURE__*/_createElementVNode("h1",null,"技术摸鱼",-1/*HOISTED*/))

const_hoisted_3=/*#__PURE__*/_withScopeId(()=>/*#__PURE__*/_createElementVNode("p",null,"今天天气真不错",-1/*HOISTED*/))

exportfunctionrender(_ctx,_cache,$props,$setup,$data,$options){

return(_openBlock(),_createElementBlock("div",_hoisted_1,[

_hoisted_2,

_hoisted_3,

_createElementVNode("div",null,_toDisplayString(_ctx.name),1/*TEXT*/)

]))

}

注意第 3 个_createElementVNode的第 4 个参数即patchFlag字段类型,字段类型情况如下所示。1 代表节点为动态文本节点,那在 diff 过程中,只需比对文本对容,无需关注 class、style等。除此之外,发现所有的静态节点,都保存为一个变量进行静态提升,可在重新渲染时直接引用,无需重新创建。

exportconstenumPatchFlags{

TEXT=1,//动态文本内容

CLASS=1



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有